package iptables

import (
	"fmt"
	"strings"
)

func AddForward(protocol, srcPort, dest, destPort, iface string, save bool) error {
	if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
		iptablesArg := fmt.Sprintf("-A %s", Chain1PanelPreRouting)
		if iface != "" {
			iptablesArg += fmt.Sprintf(" -i %s", iface)
		}
		iptablesArg += fmt.Sprintf(" -p %s --dport %s -j DNAT --to-destination %s:%s", protocol, srcPort, dest, destPort)
		if err := Run(NatTab, iptablesArg); err != nil {
			return err
		}

		if err := Run(NatTab, fmt.Sprintf("-A %s -d %s -p %s --dport %s -j MASQUERADE", Chain1PanelPostRouting, dest, protocol, destPort)); err != nil {
			return err
		}

		if err := Run(FilterTab, fmt.Sprintf("-A %s -d %s -p %s --dport %s -j ACCEPT", Chain1PanelForward, dest, protocol, destPort)); err != nil {
			return err
		}

		if err := Run(FilterTab, fmt.Sprintf("-A %s -s %s -p %s --sport %s -j ACCEPT", Chain1PanelForward, dest, protocol, destPort)); err != nil {
			return err
		}
	} else {
		iptablesArg := fmt.Sprintf("-A %s", Chain1PanelPreRouting)
		if iface != "" {
			iptablesArg += fmt.Sprintf(" -i %s", iface)
		}
		iptablesArg += fmt.Sprintf(" -p %s --dport %s -j REDIRECT --to-port %s", protocol, srcPort, destPort)
		if err := Run(NatTab, iptablesArg); err != nil {
			return err
		}
	}
	return nil
}

func DeleteForward(num string, protocol, srcPort, dest, destPort, iface string) error {
	if err := Run(NatTab, fmt.Sprintf("-D %s %s", Chain1PanelPreRouting, num)); err != nil {
		return err
	}

	if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
		if err := Run(NatTab, fmt.Sprintf("-D %s -d %s -p %s --dport %s -j MASQUERADE", Chain1PanelPostRouting, dest, protocol, destPort)); err != nil {
			return err
		}

		if err := Run(FilterTab, fmt.Sprintf("-D %s -d %s -p %s --dport %s -j ACCEPT", Chain1PanelForward, dest, protocol, destPort)); err != nil {
			return err
		}

		if err := Run(FilterTab, fmt.Sprintf("-D %s -s %s -p %s --sport %s -j ACCEPT", Chain1PanelForward, dest, protocol, destPort)); err != nil {
			return err
		}
	}
	return nil
}

func ListForward(chain ...string) ([]IptablesNatInfo, error) {
	if len(chain) == 0 {
		chain = append(chain, Chain1PanelPreRouting)
	}
	stdout, err := RunWithStd(NatTab, fmt.Sprintf("-nvL %s --line-numbers", chain[0]))
	if err != nil {
		return nil, err
	}
	var forwardList []IptablesNatInfo
	lines := strings.Split(stdout, "\n")
	for i := 0; i < len(lines); i++ {
		fields := strings.Fields(lines[i])
		if len(fields) < 13 {
			continue
		}
		item := IptablesNatInfo{
			Num:      fields[0],
			Protocol: loadProtocol(fields[4]),
			InIface:  fields[6],
			OutIface: fields[7],
			Source:   fields[8],
			SrcPort:  loadNatSrcPort(fields[11]),
		}
		if len(fields) == 15 && fields[13] == "ports" {
			item.DestPort = fields[14]
		}
		if len(fields) == 13 && strings.HasPrefix(fields[12], "to:") {
			parts := strings.Split(fields[12], ":")
			if len(parts) > 2 {
				item.DestPort = parts[2]
				item.Destination = parts[1]
			}
		}
		if len(item.Destination) == 0 {
			item.Destination = "127.0.0.1"
		}
		forwardList = append(forwardList, item)
	}

	return forwardList, nil
}

func loadNatSrcPort(portStr string) string {
	var portItem string
	if strings.Contains(portStr, "dpt:") {
		portItem = strings.ReplaceAll(portStr, "dpt:", "")
	}
	if strings.Contains(portStr, "dpts:") {
		portItem = strings.ReplaceAll(portStr, "dpts:", "")
	}
	portItem = strings.ReplaceAll(portItem, ":", "-")
	return portItem
}

type IptablesNatInfo struct {
	Num         string `json:"num"`
	Protocol    string `json:"protocol"`
	InIface     string `json:"inIface"`
	OutIface    string `json:"outIface"`
	Source      string `json:"source"`
	Destination string `json:"destination"`
	SrcPort     string `json:"srcPort"`
	DestPort    string `json:"destPort"`
}
